1 /**
2 Copyright: Copyright (c) 2018, Joakim Brännström. All rights reserved.
3 License: MPL-2
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 This Source Code Form is subject to the terms of the Mozilla Public License,
7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain
8 one at http://mozilla.org/MPL/2.0/.
9 
10 This file is under the MPL-2 license because it is code copied from deXtool.
11 
12 This file contains functions for filtering files over a regex.
13 */
14 module code_checker.engine.file_filter;
15 
16 import std.regex : Regex, regex;
17 
18 @safe:
19 
20 struct FileFilter {
21     import std.array : array;
22     import std.algorithm : map;
23 
24     Regex!char[] exclude;
25 
26     this(string[] raw_regex) {
27         foreach (a; raw_regex) {
28             try {
29                 this.exclude ~= regex(a);
30             } catch (Exception e) {
31                 throw new Exception("Bad regex:" ~ a ~ ":" ~ e.msg);
32             }
33         }
34     }
35 
36     /// Returns: true if the file matches the permissions in the file filter and thus should be used.
37     bool match(string fname) {
38         if (exclude.length > 0)
39             return !matchAny(fname, exclude);
40         return true;
41     }
42 }
43 
44 auto fileFilter(Range)(Range r, string[] raw_regex) {
45     import std.algorithm : filter;
46 
47     auto ff = FileFilter(raw_regex);
48     return r.filter!(a => ff.isOk(a));
49 }
50 
51 /// Returns: true if the value match any regex.
52 bool matchAny(const string value, Regex!char[] re) @safe nothrow {
53     import std.algorithm : canFind;
54     import std.regex : matchFirst, RegexException;
55 
56     bool passed = false;
57 
58     foreach (ref a; re) {
59         try {
60             auto m = matchFirst(value, a);
61             if (!m.empty && m.pre.length == 0 && m.post.length == 0) {
62                 passed = true;
63                 break;
64             }
65         } catch (Exception ex) {
66         }
67     }
68 
69     return passed;
70 }
71 
72 version (unittest) {
73     import unit_threaded : shouldBeTrue;
74 }
75 
76 @("Shall match all regex")
77 @safe unittest {
78     import std.regex : regex;
79 
80     Regex!char[] re = [regex(".*/foo/.*"), regex(".*/src/.*")];
81 
82     matchAny("/p/foo/more/src/file.c", re).shouldBeTrue;
83 }